#ifndef __DICOP_H
#define __DICOP_H

#ifdef WIN32
#include <windows.h>
#else
#include <semaphore.h>
#endif

#include <dicom/list.h>
#include <dicom/queue.h>


#ifdef WIN32
#undef strdup
#define strdup _strdup
#define sleep(s) Sleep(s * 1000)
#define bzero(dest, size) memset(dest, 0, size);
#define snprintf _snprintf
#define SOL_TCP IPPROTO_TCP
#define __func__ __FUNCTION__
#endif


#define DICOP_PORT            1225
#define DICOP_MAX_CONN        10
#define DICOP_MAX_ARG_NBR     32
#define DICOP_BUF_SZ          4096  /* FIXME: 4K is enough for a full packet? */

#define callback_t struct list_head *
#define callback_args_t struct list_head *


struct dicop_app {
    struct list_head app_node;

    char *app_name;
    int server_id;                     /* Server ID used by client */
    int client_id[DICOP_MAX_CONN];     /* Client IDs used by server */
    int client_count;
    struct list_head meth_desc_list;   /* List for struct dicop_method_desc */
    struct list_head sig_desc_list;    /* List for struct dicop_signal_desc */
};

struct dicop_method_desc {
    struct list_head desc_node;

    char *name;
    callback_t (*callback)(callback_args_t);
    int arg_nbr;
    int rep_nbr;
    struct dicop_app *app;
    struct list_head arg_list;
    struct list_head rep_list;
};

struct dicop_signal_desc {
    struct list_head desc_node;

    char *name;
    callback_t (*callback)(callback_args_t);
    int arg_nbr;
    struct dicop_app *app;
    struct list_head arg_list;
};

struct dicop_method_data {
    struct list_head data_node;

    char *name;
    char type;
#define DICOP_METHOD_TYPE_CALL   1
#define DICOP_METHOD_TYPE_REPLY  2
    struct list_head arg_list;
};

struct dicop_signal_data {
    struct list_head data_node;

    char *name;
    struct list_head arg_list;
};

struct dicop_args {
    struct list_head arg_node;

    char *name;
    char *value;
};

struct dicop_message {
    int conn_id;                       /* sockfd for client */
    unsigned int session_id;                    /* Session ID (by time()) */
#ifdef WIN32
    HANDLE session_sem;
#else
    sem_t session_sem;                 /* Semaphore for session queue */
#endif
    int error;
    char *app_name;
    int type;
#define DICOP_MESSAGE_TYPE_INITIAL 0   /* Initial Type */
#define DICOP_MESSAGE_TYPE_DEF_REQ 1   /* DefRequest */
#define DICOP_MESSAGE_TYPE_DEF_REP 2   /* DefReply */
#define DICOP_MESSAGE_TYPE_CONTEXT 3   /* Context */
#define DICOP_MESSAGE_TYPE_UNKNOWN 4   /* Unknown Type */

    struct dicop_app *app;             /* Indicate which app this message is for */

    struct list_head meth_desc_list;   /* List for struct dicop_method_desc */
    struct list_head sig_desc_list;    /* List for struct dicop_signal_desc */

    struct list_head meth_data_list;   /* List for struct dicop_method_data */
    struct list_head sig_data_list;    /* List for struct dicop_signal_data */
};

#define DICOP_OK 1

#define DICOP_ERROR_SYSTEM       -1
#define DICOP_ERROR_ARGUMENTS    -2
#define DICOP_ERROR_PROTOCOL     -3
#define DICOP_ERROR_PROT_FORMAT  -4
#define DICOP_ERROR_PROT_ROOT    -5
#define DICOP_ERROR_MEMORY       -6
#define DICOP_ERROR_CONNECTION   -7
#define DICOP_ERROR_APP          -8
#define DICOP_ERROR_METHOD_CALL  -9
#define DICOP_ERROR_METHOD_REPLY -10
#define DICOP_ERROR_SIGNAL       -11
#define DICOP_ERROR_TYPE         -12


#define INTERNAL_CALL static


/* DiCop API prototype */
void dicop_init(void);
void dicop_release(void);

struct dicop_app *dicop_app_new(char *app_name);
void dicop_app_destroy(struct dicop_app *app);
struct dicop_app *dicop_app_query(int server_id, char *app_name);

struct dicop_method_desc *dicop_method_new(struct dicop_app *app, char *method_name);
void dicop_method_callback_install(struct dicop_method_desc *desc, callback_t (*callback)(callback_args_t));
int dicop_method_args_init(struct dicop_method_desc *desc, unsigned int arg_nbr, ...);
int dicop_method_reply_init(struct dicop_method_desc *desc, unsigned int rep_nbr, ...);
int dicop_method_call_real(int wait, struct dicop_app *app, char *method_name, ...);
int dicop_method_call_get_reply(struct dicop_app *app, char *method_name, int session_id, ...);

#ifdef WIN32
#define dicop_method_call(...) dicop_method_call_real(1, __VA_ARGS__)
#define dicop_method_call_no_reply(...) dicop_method_call_real(0, __VA_ARGS__)
#else
#define dicop_method_call(args...) dicop_method_call_real(1, ## args)
#define dicop_method_call_no_reply(args...) dicop_method_call_real(0, ## args)
#endif

struct dicop_signal_desc *dicop_signal_new(struct dicop_app *app, char *signal_name);
int dicop_signal_args_init(struct dicop_signal_desc *desc, unsigned int arg_nbr, ...);
int dicop_signal_connect(struct dicop_app *app, char *signal_name, callback_t (*callback)(callback_args_t));
int dicop_signal_emit(struct dicop_signal_desc *desc, ...);

int dicop_server_create(int port);
int dicop_server_terminate(void);

int dicop_client_connect(char *serv_ip, int serv_port);
void dicop_client_disconnect(int sockfd);

void dicop_callback_get_args(void *args, int arg_nbr, ...);
struct list_head *dicop_callback_put_reply(int rep_nbr, ...);
struct list_head *dicop_callback_null_reply(void);

void dicop_error_print(int err);

#endif

